package com.gz.springmvc.servlet;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gz.entity.Monster;
import com.gz.handler.GzHandler;
import com.gz.springmvc.annotation.Controller;
import com.gz.springmvc.annotation.RequestMapping;
import com.gz.springmvc.annotation.RequestParam;
import com.gz.springmvc.annotation.ResponseBody;
import com.gz.springmvc.context.GzWebApplicationContext;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Handler;

public class GzDispatcherServlet extends HttpServlet {

    //将具有RequestMapping的方法保存到handlerList
    private List<GzHandler> handlerList=new ArrayList<>();
    GzWebApplicationContext gzWebApplicationContext = null;

    @Override
    public void init() throws ServletException {
        //获取web.xml中的contextConfigLocation
        /*
        <init-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>classpath:gzspringmvc.xml</param-value>
        </init-param>
         */
        String configLocation = getServletConfig().
                getInitParameter("contextConfigLocation");//classpath:gzspringmvc.xml

        String[] split = configLocation.split(":");
        configLocation = split[1];//gzspringmvc.xml
        gzWebApplicationContext = new GzWebApplicationContext(configLocation);
        gzWebApplicationContext.init();

        //调用initHandlerMapping完成对url和控制器方法的映射
        initHandlerMapping();
        //输出handlerList
        //handlerList= [GzHandler{url='/monster/list',
        //controller=com.gz.controller.MonsterHandler@63160592,
        //method=public void com.gz.controller.MonsterHandler.listMonsters(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)}]
        System.out.println("handlerList= "+handlerList);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //调用方法完成请求转发
        executeDispatch(request, response);
    }


    private void initHandlerMapping() {
        if (gzWebApplicationContext.ioc.isEmpty()) {
            return;
        }else {
            for(Map.Entry<String,Object>entry:gzWebApplicationContext.ioc.entrySet()){
                //先取出注入的Object对象
                Class<?> clazz = entry.getValue().getClass();
                if(clazz.isAnnotationPresent(Controller.class)){
                    Method[] declaredMethods = clazz.getDeclaredMethods();
                    for(Method method:declaredMethods){
                        //这里与原生的springmvc有区别
                        //这里默认RequestMapping只在方法上
                        if (method.isAnnotationPresent(RequestMapping.class)){
                            String url = method.getAnnotation(RequestMapping.class).value();
                            //创建Handler对象
                            GzHandler gzHandler = new GzHandler(url, entry.getValue(), method);
                            handlerList.add(gzHandler);
                        }
                    }
                }
            }
        }
    }

    private GzHandler getGzHandler(HttpServletRequest request) {
        //获取用户请求的uri,比如http://localhost:8080/springmvc/monster/list?name=xxx
        //uri = /springmvc/monster/list
//        System.out.println("request.getContextPath()= "+request.getContextPath()); /springmvc
        //request.getContextPath()与getServletContext().getContextPath()结果相同
        String prefix = request.getContextPath();
        String requestURI = request.getRequestURI();
        for (GzHandler handler : handlerList) {
            if(requestURI.equals(prefix+handler.getUrl())){
//                System.out.println("requestURI= "+requestURI); /springmvc/monster/list
//                System.out.println("prefix+handler.getUrl()= "+prefix+handler.getUrl());
                // /springmvc/monster/list
                return handler;
            }
        }
        return null;
    }

    //编写方法完成分发请求任务
    private void executeDispatch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        GzHandler gzHandler = getGzHandler(request);
        if(gzHandler==null){//说明用户请求的路径/资源不存在
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
        }else { //匹配成功
            //1.得到目标方法的形参参数信息[对应的数组]
            Class<?>[] parameterTypes = gzHandler.getMethod().getParameterTypes();
            //2.创建一个实参数组
            Object[] params = new Object[parameterTypes.length];
            //遍历形参数组，根据形参数组信息，将实参填充到实参数组
            for (int i = 0; i < parameterTypes.length; i++) {
                Class<?> parameterType = parameterTypes[i];
                //如果这个形参是HttpServletRequest,将request填充到params
                if("HttpServletRequest".equals(parameterType.getSimpleName())){
                    params[i]=request;
                } else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {
                    params[i]=response;
                }
            }

            //Map<String, String[]>
            //String:参数名
            //String[]对应参数值，因为一个参数名可能有多个值 如xxx?name=xx&name=xx用于复选框等
            Map<String, String[]> parameterMap = request.getParameterMap();
            for(Map.Entry<String,String[]> entry:parameterMap.entrySet()){
                String name=entry.getKey();
                //这里做了简化，只考虑一个参数名对应一个值，不考虑复选框等情况
                //但考虑复选框等情况也不难
                String value=entry.getValue()[0];
                int requestParamterIndex = getRequestParamterIndex(gzHandler.getMethod(), name);
                if (requestParamterIndex == -1) {
                    //没有找到，使用默认机制匹配,即按照形参名匹配
                    //如果没有@RequestParam，但形参匹配，就把实参填充
                    ArrayList<String> requestParamNames = getRequestParamNames(gzHandler.getMethod());
                    for (int i = 0; i <requestParamNames.size() ; i++) {
                        if (name.equals(requestParamNames.get(i))) {
                            params[i]=value;
                            break;
                        }
                    }

                }else{
                    params[requestParamterIndex] = value;
                }
            }
            try {
                Object result = gzHandler.getMethod().invoke(
                        gzHandler.getController(), params);
                //这里就是对返回的结果进行解析=>原生的springmvc可以通过视图解析器完成
                //这里只是写了核心机制
                if (result instanceof String){
                    String viewName=(String)result;
                    //如果是forward:login_ok.jsp 或 redirect:xxx
                    if(viewName.contains(":")){
                        String viewType=viewName.split(":")[0];
                        String viewPage=viewName.split(":")[1];//要跳转的页面
                        if ("forward".equals(viewType)) {
                            request.getRequestDispatcher(viewPage).forward(request, response);
                        }else if ("redirect".equals(viewType)) {
                            response.sendRedirect(viewPage);
                        }
                    }else {
                        request.getRequestDispatcher(viewName).forward(request, response);
                    }
                } else if (result instanceof ArrayList) {//这里简单写一个类
                    Method method = gzHandler.getMethod();
                    if (method.isAnnotationPresent(ResponseBody.class)) {
                        //将arraylist转为json，可以使用gson,也可以使用jackson，都要引包
                        //这里使用Jackson
                        ObjectMapper objectMapper = new ObjectMapper();
                        //这样就变为json格式了
                        String json = objectMapper.writeValueAsString(result);
                        response.setContentType("text/html;charset=utf-8");
                        response.getWriter().write("<h1>"+json+"</h1>");//返回到页面显示
                    }
                }

                System.out.println("方法调用成功");
            } catch (Exception e) {
               e.printStackTrace();
            }
        }
    }

    public int getRequestParamterIndex(Method method,String name) {
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            if(parameter.isAnnotationPresent(RequestParam.class)){
                String value = parameter.getAnnotation(RequestParam.class).value();
                if(value.equals(name)){
                    return i;
                }
            }
        }
        return -1;
    }

    public ArrayList<String> getRequestParamNames(Method method) {
        Parameter[] parameters = method.getParameters();
        ArrayList<String> names = new ArrayList<>();
        for (Parameter parameter : parameters) {
            //注意这里parameter.getName()必须要在pom.xml中配置插件才行
            //否则parameter.getName()的值就是arg0,arg1...
//            System.out.println("parameter.getName()= "+parameter.getName());
            names.add(parameter.getName());
        }
        return names;
    }
}
